package se.liu.oschi129.world.viewcontrol;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;

import javax.swing.JComponent;

import se.liu.oschi129.world.interfaces.InputAction;
import se.liu.oschi129.world.interfaces.WorldListener;
import se.liu.oschi129.world.model.*;
import se.liu.oschi129.world.model.WorldInput.ActionType;
import se.liu.oschi129.world.objects.WorldObject;

/**
 * This component can be used to display a world object. 
 * 
 * @author oschi129
 */
public class WorldViewController extends JComponent implements WorldListener {
	
	// Auto-generated serial version UID for this component
	private static final long serialVersionUID = -7411488586071635353L;
	
	// The world used in this component
	private World world;
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - - - Constructors - - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	public WorldViewController(World world) {
		this.world = world;
		
		// Turns on double buffering
		setDoubleBuffered(true);
		
		// Adds a listener to the world
		world.addListener(this);
		
		// Adds a keyboard listener
		addKeyListener(new KeyboardActionListener());
	}
	
	/**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - - Public methods - - - - - - - - - - 
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	/** This component is always in focus */
	@Override public boolean isFocusTraversable() {
		return true;	
	}
	
	/** Paint the world in the component */
	@Override public void paintComponent(Graphics g) {
		
		// Fill the component with a black background
		Graphics2D g2d = (Graphics2D) g;
		g2d.setColor(Color.BLACK);
		g2d.fillRect(0,0,getWidth(),getHeight());
		
		// Translate the drawing area to display the world view
		AffineTransform transform = new AffineTransform();
		WorldView worldView = world.getView();
		
		// The view returns integer so we have to cast them into doubles before dividing
		transform.scale(getWidth()/(double) worldView.getViewWidth(), getHeight()/(double) worldView.getViewHeight());
		transform.translate(-worldView.getViewX(),-worldView.getViewY());
		g2d.setTransform(transform);
		Rectangle2D view = new Rectangle2D.Double(worldView.getViewX(),worldView.getViewY(),worldView.getViewWidth(),worldView.getViewHeight());
		
		/* Create a WorldGraphics instance linked to the graphics2d object.
		 * This object translated the graphics2D to a model which is
		 * "understandable" for the WorldObjects.
		 */
		
		WorldGraphics worldg = new WorldGraphics(g2d);
		
		synchronized (world.getObjectLock()) {
			for (WorldObject obj : world.getObjects()) {
				if (obj.getVisible() && obj.intersects(view))
					obj.performDrawingAction(worldg);
			}
		}

	}
	
	/** Implementation of the WorldListener interface */
	public void notifyWorldChanged() {
		repaint();
	}
	
	 /**
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 * - - - - - - - - - - Private classes - - - - - - - - - - -
	 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	 */
	
	/** This private class listens for keyboard strokes which are sent to the interactive objects in the world */
	private class KeyboardActionListener extends KeyAdapter {
		
		@Override public void keyPressed(KeyEvent event) {
			for (InputAction interactiveObject : world.getInteractiveObjects()) {
				synchronized (world.getInteractiveObjectLock()) {
					interactiveObject.performInputAction(new WorldInput(event, ActionType.PRESSED));
				}
			}
		}

		@Override public void keyReleased(KeyEvent event) {
			for (InputAction interactiveObject : world.getInteractiveObjects()) {
				synchronized (world.getInteractiveObjectLock()) {
					interactiveObject.performInputAction(new WorldInput(event, ActionType.RELEASED));
				}
			}
		}

		@Override public void keyTyped(KeyEvent event) {
			for (InputAction interactiveObject : world.getInteractiveObjects()) {
				synchronized (world.getInteractiveObjectLock()) {
					interactiveObject.performInputAction(new WorldInput(event, ActionType.TYPED));
				}
			}
		}
		
	}
	
}
